# Usage: process.tcl [-?] [-d] [-u] [--] variant.ps game.txt [game.ps]umgrp

package require Iexpect

namespace eval Process {
	
	proc informIf {show msg} {
		if {[uplevel expr $show]} {puts [uplevel list $msg]}
		return 0
	}
	
	proc mention {error {info {}}} {
		puts "Error: $error"
		if {[llength $info]} {
			puts [join $info "\n"]
		}
		return 0
	}
	
	proc fileCheck {fileName fileExt hasToExist} {
		if {![string equal -nocase ".$fileExt" [file extension $fileName]]} {
			return [mention "Expected file extension $fileExt for file: $fileName"]
		}
		if {$hasToExist && ![file exists $fileName]} {
			return [mention "File does not exist: $fileName"]
		}
		return 1
	}
	
	proc formatPower {power {how 2}} {
		variable commands
		set power [regsub -all "(^|\\s)_" $power "\\1"]
		if {[llength $power] < 2} {
			return $power
		} else {
			switch $how 0 {
				return [lindex $power 0]
			} 1 {
				return " [lindex $power 0]"
			} 2 {
				return "[lindex $power 0] ([lindex $power 1])"
			} 3 {
				if {$commands(Controls)} {
					return "[lindex $power 1] Controls [lindex $power 0]"
				} elseif {$commands(ControlledBy)} {
					return "[lindex $power 0] ControlledBy [lindex $power 1]"
				} else {
					return [lindex $power 0]
				}
			} default {
				return $power
			}
		}
	}
	
	proc powerSort {array {how 2}} {
		upvar $array arr
		switch $how 1 {
			set sortPowers []
			foreach power [array names arr] {
				lappend sortPowers [lreverse $power]
			}
			set sortPowers [lsort $sortPowers]
			set powers []
			foreach power $sortPowers {
				lappend powers [lreverse $power]
			}
		} default {
			set powers [lsort [array names arr]]
		}
		if {[set index [lsearch $powers UNOWNED]] != -1} {
			set powers [lreplace $powers $index $index]
			lappend powers UNOWNED
		}
		return $powers
	}
	
	proc getTerritory {province} {
		return [lindex [split $province "/"] 0]
	}
	
	proc getCoast {province} {
		return [lindex [split $province "/"] 1]
	}
	
	proc isVisibleProvince {province} {
		return [expr ![string equal $province "???"]]
	}
	
	proc checkCountry {unit province} {
		variable country
		
		if {![info exists country]} {
			return [mention "Country not set for unit $unit $province"]
		}
		return 1
	}
	
	# province:
	#   must be visible, must be part of territories array, including coasts
	# command:
	#   empty = hold/no action
	#   non-empty: must be part of commands array
	# destinations:
	#   must all be visible, must all be part of territories array, including coasts
	# success:
	#	-1: action not yet executed (e.g. proposed retreat)
	#    0: action failed
	#    1: action succeeded
	# phase:
	#    m: move
	#    r: retreat
	#    a: adjustment
	# transition:
	#    3: neither arrive nor depart (non-order)
	#    2: arrive from nowhere where other unit may depart
	#    1: arrive from nowhere in free spot
	#    0: stay
	#   -1: move to last province in destinations
	#   -2: depart to unknown destination
	#   -3: depart to unknown destination from a spot now occupied by a different unit
	#   -4: depart off the board (destroyed unit)
	proc displayAction {country unit province command destinations success phase transition} {
		variable displayInfo; variable territories; variable occupations; variable multis; variable units; variable draws; variable commands; variable arrows; variable survivors; variable ownerStatus; variable victims; variable disbands; variable unknowns
		
		informIf $displayInfo {[formatPower $country] $unit $province $command $destinations $success $phase $transition}
		if {![isVisibleProvince $province]} {
			return [mention "Province not set for unit $unit of country $country"]
		} elseif {![info exists territories($province)]} {
			return [mention "Unknown province $province"]
		} else {
			foreach destination $destinations {
				if {![isVisibleProvince $destination]} {
					return [mention "Destination not set for unit $unit $province"]
				} elseif {![info exists territories($destination)]} {
					return [mention "Unknown destination $destination"]
				}
			}
		}
	
		set territory [getTerritory $province]
		set preTransition 3
		if {[info exists occupations($territory)]} {
			set preTransition [lindex $occupations($territory) 1]
		}
		switch -- $transition 3 {
			set preTransition 3
		} 2 {
			set preTransition 3
		} -3 {
			set preTransition 3
		} default {
			if {[info exists occupations($territory)]} {
				if {![string equal [lindex $occupations($territory) 0] "$country $unit"]} {
					return [mention "Territory $territory already occupied by [lindex $occupations($territory) 0]"]
				}		
				switch -- $preTransition 3 {
				} 1 {
					return [mention "No preliminary action allowed for $unit $province arriving in an empty spot"]
				} -1 {
					return [mention "No second action allowed for $unit $province after moving to different spot"]
				} -2 {
					switch -- $transition 0 {
						switch -- $success -1 {
						} default {
							return [mention "The only actions allowed for $unit $province after departing are to retreat to a different spot or off the board"]
						}
					} -1 {
					} -2 {
					} -4 {
					} default {
						return [mention "The only actions allowed for $unit $province after departing are to retreat to a different spot or off the board"]
					}
				} -4 {
					return [mention "No second action allowed for $unit $province after being destroyed"]
				}
			}
			if {![info exists multis($territory)]} {
				set occupations($territory) [list "$country $unit" $transition]
			}
			switch -- $preTransition 3 {
				lappend units($country) "$territories($province) $draws($unit)"
			}
		}
		
		if {[string length $command]} {
			if {$commands($command)} {
				set params $territories($province)
				foreach destination $destinations {
					eval lappend params $territories($destination)
				}
				lappend arrows [list "$params $command" [expr $success == 1]]
			}
		}
		
		if {$ownerStatus} {
			switch -- $transition 3 {
			} 2 {
				incr survivors($country)
			} 1 {
				incr survivors($country)
			} -3 {
				incr survivors($country)
			} -4 {
				switch -- $preTransition 1 {
					incr survivors($country) -1 
				} 0 {
					incr survivors($country) -1 
				} -2 {
					incr survivors($country) -1 
				}
			} default {
				switch -- $preTransition 1 {
				} 0 {
				} -2 {
				} default {
					incr survivors($country)
				}
			}
		} else {
			switch -- $transition 3 {
			} 2 {
			} 1 {
			} -3 {
				incr survivors($country)
			} default {
				switch -- $preTransition 0 {
				} -2 {
				} default {
					incr survivors($country)
				}
			}
		}
		
		switch -- $transition -4 {
			switch -- $phase m {
				lappend victims $province
			} r {
				lappend disbands $province
			}
		} 
		
		switch -- $success -1 {
			if {![info exists unknowns]} {
				set unknowns [list $province]
			} elseif {[lsearch $unknowns $province] == -1} {
				lappend unknowns $province
			}
		}
		
		return 1
	}
	
	proc adjust {unit province operator} {
		variable country; variable orders; variable adjustments; variable builds; variable season
	
		if {![checkCountry $unit $province]} {return 0}
		switch $operator "!" {
			if {![displayAction $country $unit $province {} {} 1 a 0]} {return 0}
			lappend orders($country) [list "$unit $province" ""]
		} "?" {
			if {![displayAction $country $unit $province {} {} -1 a -3]} {return 0}
			lappend orders($country) [list "$unit $province ?" ""]
		} "+" {
			if {[info exists builds($country)] && !$builds($country)} {
				mention "Can't build and remove in the same season for power $country"
			} else {
				if {![displayAction $country $unit $province BuildUnit {} 1 a [expr [string equal $season Fall] ? 2 : 1]]} {return 0}
				set builds($country) 1
				lappend adjustments($country) "$unit $province"
			}
		} "*" {
			if {[info exists builds($country)] && $builds($country)} {
				mention "Can't build and remove in the same season for power $country"
			} else {
				if {![displayAction $country $unit $province RemoveUnit {} 1 a -4]} {return 0}
				set builds($country) 0
				lappend adjustments($country) "$unit $province"
				lappend orders($country) [list "$unit $province" ""]
			}
		} "!!" {
			if {![displayAction $country $unit $province FindUnit {} 1 m 1]} {return 0}
			lappend orders($country) [list "$unit $province FOUND" ""]
		} "??" {
			if {![displayAction $country $unit $province LoseUnit {} 1 m -2]} {return 0}
			lappend orders($country) [list "$unit $province LOST" ""]
		}
		
		return 1
	}
	
	proc hold {unit province operator} {
		variable country; variable orders; variable holdResults
	
		if {![checkCountry $unit $province]} {return 0}
		if {![displayAction $country $unit $province {} {} 1 m [expr [string equal $operator "~"] ? -2 : [string equal $operator "*"] ? -4 : 0]]} {return 0}
		lappend orders($country) [list "$unit $province H" $holdResults($operator)]
	
		return 1
	}
	
	proc move {unit province destination operator} {
		variable country; variable orders; variable moveResults; variable territories
	
		if {![checkCountry $unit $province]} {return 0}
		set provinceKnown [isVisibleProvince $province]
		set destinationKnown [isVisibleProvince $destination]
		if {$provinceKnown && ![info exists territories($province)]} {
			return [mention "Unknown province $province"]
		} elseif {$destinationKnown && ![info exists territories($destination)]} {
			return [mention "Unknown destination $destination"]
		} elseif {!$provinceKnown && !$destinationKnown} {
			return [mention "Neither province nor destination set for unit $unit of country $country"]
		}
		
		set cmd ArrowMove
		set prv $province
		set dst [list $destination]
		set trn -1
		if {!$destinationKnown} {
			set cmd ArrowDepart
			set dst {}
			set trn -2
		} elseif {!$provinceKnown} {
			set cmd ArrowArrive
			set prv $destination
			set dst {}
			set trn 2
		}
		if {![displayAction $country $unit $prv $cmd $dst [string equal $operator "!"] m [expr [string equal $operator "!"] ? $trn : [string equal $operator "~"] ? -2 : [string equal $operator "*"] ? -4 : 0]]} {return 0}
		lappend orders($country) [list "$unit $province - $destination" $moveResults($operator)]
	
		return 1
	}
	
	proc supportHold {unit province origin operator} {
		variable country; variable orders; variable supportResults
	
		if {![checkCountry $unit $province]} {return 0}
	
		if {![displayAction $country $unit $province ArrowHold [list $origin] [string equal $operator "!"] m [expr [string equal $operator "~"] ? -2 : [string equal $operator "*"] ? -4 : 0]]} {return 0}
		lappend orders($country) [list "$unit $province S $origin" $supportResults($operator)]
		
		return 1
	}
	
	proc supportMove {unit province origin destination operator} {
		variable country; variable orders; variable supportResults
	
		if {![checkCountry $unit $province]} {return 0}
	
		if {![displayAction $country $unit $province ArrowSupport [list $origin $destination] [string equal $operator "!"] m [expr [string equal $operator "~"] ? -2 : [string equal $operator "*"] ? -4 : 0]]} {return 0}
		lappend orders($country) [list "$unit $province S $origin - $destination" $supportResults($operator)]
		
		return 1
	}
	
	proc convoy {unit province origin destination operator} {
		variable country; variable orders; variable convoyResults
	
		if {![checkCountry $unit $province]} {return 0}
	
		if {![displayAction $country $unit $province ArrowConvoy [list $origin $destination] [string equal $operator "!"] m [expr [string equal $operator "~"] ? -2 : [string equal $operator "*"] ? -4 : 0]]} {return 0}
		lappend orders($country) [list "$unit $province C $origin - $destination" $convoyResults($operator)]
		
		return 1
	}
	
	proc retreat {unit province destinations operator} {
		variable country; variable retreats; variable moveResults; variable territories
	
		if {![checkCountry $unit $province]} {return 0}
		
		switch $operator "!" {
			if {[llength $destinations] == 0} {
				return [mention "Expected retreat destination for unit $unit $province"]
			} elseif {[llength $destinations] > 1} {
				return [mention "Expected only one retreat destination for unit $unit $province"]
			}
		} "*" {
			if {[llength $destinations] == 0} {
				if {![displayAction $country $unit $province {} {} 0 r -4]} {return 0}
				lappend retreats($country) [list "$unit $province DISBAND" ""]
				
				return 1
			} elseif {[llength $destinations] > 1} {
				return [mention "Expected at most one retreat destination for unit $unit $province"]
			}
		} "?" {
			if {[llength $destinations] == 0} {
				return [mention "Expected at least one retreat destination for unit $unit $province"]
			}
	
			foreach destination $destinations {
				if {![displayAction $country $unit $province ArrowPropose [list $destination] -1 r 0]} {return 0}
			}
			lappend retreats($country) [list "$unit $province - [string trim $destinations] ?"]
			
			return 1
		}
	
		set provinceKnown [isVisibleProvince $province]
		if {$provinceKnown && ![info exists territories($province)]} {
			return [mention "Unknown province $province"]
		}
		set destination [lindex $destinations 0]
		set destinationKnown [isVisibleProvince $destination]
		if {$destinationKnown && ![info exists territories($destination)]} {
			return [mention "Unknown destination $destination"]
		} elseif {!$provinceKnown && !$destinationKnown} {
			return [mention "Neither province nor destination set for unit $unit of country $country"]
		}
	
		set cmd ArrowRetreat
		set prv $province
		set dst [list $destination]
		set trn -1
		if {!$destinationKnown} {
			if {$commands(ArrowRetreatDepart)} {
				set cmd ArrowRetreatDepart
			} else {
				set cmd ArrowDepartRetreat
			}
			set dst {}
			set trn -2
		} elseif {!$provinceKnown} {
			if {$commands(ArrowRetreatArrive)} {
				set cmd ArrowRetreatArrive
			} else {
				set cmd ArrowArriveRetreat
			}
			set prv $destination
			set dst {}
			set trn 2
		}
		if {![displayAction $country $unit $prv $cmd $dst [string equal $operator "!"] r [expr [string equal $operator "!"] ? $trn : -4]]} {return 0}
		lappend retreats($country) [list "$unit $province - $destination" $moveResults($operator)]
		
		return 1
	}
	
	proc showPage {} {
		variable g; variable pages; variable bbox; variable title; variable season; variable country
		variable territories; variable centers; variable neutrals; variable supplies; variable powers
		variable units; variable orders; variable arrows; variable victims; variable survivors; 
		variable disbands; variable adjustments; variable builds; variable retreats; variable unknowns; 
		variable actions; variable commands; variable occupations
		
		incr pages
		puts $g "%%Page: $pages $pages"
		puts $g "%%PageBoundingBox: $bbox"
		puts $g "DrawMap\nDrawNames\n($title) DrawTitle\n"
		set title ""
	
	#	if {[llength $neutrals] > 0} {
	#		eval lappend supplies(UNOWNED) $neutrals
	#	}
		foreach power [array names survivors] {
			if {($survivors($power) > 0) && ![info exists supplies($power)]} {
				set supplies($power) {}
			}
		}
	
		if {[info exists country]} {
			unset country
		}
	
		if {[info exists units]} {
			foreach power [powerSort units] {
				puts $g [formatPower $power 3]
				foreach unit $units($power) {
					puts $g $unit
				}
			}
			puts $g ""
			unset units
		}
		if {[info exists occupations]} {unset occupations}
		
		if {$commands(QueryUnit) && [info exists unknowns]} {
			foreach power [powerSort unknowns] {
				puts $g [formatPower $power 3]
				foreach unknown $unknowns($power) {
					puts $g "$territories($unknown) QueryUnit"
				}
			}
			puts $g ""
			unset unknowns
		}
	
		if {[info exists arrows]} {
			foreach arrow $arrows {
				if {[lindex $arrow 1]} {
					puts $g [lindex $arrow 0]
				} else {
					puts $g "FailedOrder [lindex $arrow 0] OkOrder"
				}
			}
			puts $g ""
			unset arrows
		}
	
		if {[info exists victims]} {
			if {$commands(DestroyUnit)} {
				foreach victim $victims {
					puts $g "$territories($victim) DestroyUnit"
				}
			}
			unset victims
		}
	
		if {[info exists disbands]} {
			if {$commands(DisbandUnit)} {
				foreach disband $disbands {
					puts $g "$territories($disband) DisbandUnit"
				}
			}
			unset disbands
		}
	
		if {[info exists orders]} {
			puts $g "OrderReport"
			foreach power [powerSort orders 2] {
				puts $g "([formatPower $power 2]) WriteOrder"
				foreach order $orders($power) {
					puts $g [format "( %-20s %s) WriteOrder" [lindex $order 0] [lindex $order 1]]
				}
			}
			puts $g ""
			unset orders
		}
	
		if {[info exists retreats]} {
			puts $g "RetreatReport"
			foreach power [powerSort retreats 0] {
				foreach retreat $retreats($power) {
					puts $g [format "(%-10s %-14s %s) WriteRetreat" [formatPower $power 0] [lindex $retreat 0] [lindex $retreat 1]]
				}
			}
			puts $g ""
			unset retreats
		}
	
		if {[info exists adjustments]} {
			puts $g "AdjustReport"
			foreach power [powerSort adjustments 0] {
				puts $g [format "(%-10s %-7s %s) WriteAdjust" [formatPower $power 0] $actions($builds($power)) [join $adjustments($power) ", "]]
			}
			puts $g ""
			unset adjustments
			unset builds
		}
	
		if {[info exists supplies]} {
			puts $g "OwnerReport"
			foreach power [powerSort supplies 1] {
				if {![string equal $power UNOWNED]} {
					puts $g [format "(%-10s %-7s %s) WriteOwner" [formatPower $power 1] [format "(%d/%d)" $survivors($power) [llength $supplies($power)]] [lsort $supplies($power)]]
				} else {
					puts $g [format "(%-18s %s) WriteOwner" [formatPower $power 1] [lsort $supplies($power)]]
				}
			}
			puts $g ""
			foreach power [powerSort supplies] {
				puts $g "[formatPower $power 3]"
				foreach supply $supplies($power) {
					puts $g "$supply supply"
				}
			}
			puts $g ""
			unset supplies
			set neutrals $centers
		}
	
		puts $g "Black\nShowPage"
		foreach power $powers {set survivors($power) 0}
	}
	
	proc process {variantFile gameRecordFile {gameFile {}} {display 0} {unsupported 0}} {
		variable g; variable pages; variable bbox; variable title; variable season; variable country
		variable territories; variable centers; variable neutrals; variable supplies; variable powers
		variable units; variable orders; variable arrows; variable victims; variable survivors;
		variable disbands; variable adjustments; variable builds; variable retreats; variable unknowns;
		variable actions; variable commands; variable occupations; variable multis
		variable draws; variable actions; variable ownerStatus;
		variable holdResults; variable moveResults; variable supportResults; variable convoyResults
		variable displayInfo $display; variable unsupportedInfo $unsupported
		
		if {![fileCheck $variantFile ps 1]} return
		if {![fileCheck $gameRecordFile txt 1]} return
		if {[string length $gameFile]} {
			if {![fileCheck $gameFile ps 0]} return
		} else {
			set gameFile "[file rootname $gameRecordFile].ps"
		}
		
		set encoding {}
		if {[string first "_" [file rootname $variantFile]] != -1} {
			set encoding "iso8859-1"
		}
		
		iexpect::Expect I -file $variantFile -white -encoding $encoding
		
		set prolog ""
		set bbox "0 0 792 612"
		array unset territories *
		array unset commands *
		array unset supplies *
		array unset survivors *
		array unset occupations *
		array unset multis *
		array unset draws *
		array unset units *
		array unset orders *
		array unset builds *
		array unset adjustments *
		array unset retreats *
		array unset unknowns *
		array unset holdResults *
		array unset moveResults *
		array unset supportResults *
		array unset convoyResults *
		set centers ""
		set powers "UNOWNED"
		set drawNames 1
		array unset commands *
		foreach cmd {
			ArrowMove ArrowHold ArrowSupport ArrowConvoy ArrowRetreat ArrowPropose
			ArrowArrive ArrowDepart ArrowRetreatArrive ArrowRetreatDepart ArrowArriveRetreat ArrowDepartRetreat
			BuildUnit RemoveUnit DestroyUnit DisbandUnit QueryUnit FindUnit LoseUnit
			Controls ControlledBy
		} {
			set commands($cmd) 0
		}
		
		I expect fill "% INFO\n" {
			append prolog [I get output]
			I expect fill "% (\[-+.0-9\]+) (\[-+.0-9\]+) (\[^ \]+)" {
				append prolog [I get output]
				set territories([string toupper [I get match 3]]) "[I get match 1] [I get match 2]"
				I continue
			} "% MAP" {
		 		append prolog [I get output]
			}
			I continue
		} "%%Page:" {
			append prolog [string trimright [I get unmatched]]
		} "%\[^\n\]*" {
			append prolog [I get output]
			I continue
		} "/(\[^ \]+) \[\{\] ?\[-+.0-9\]+ \[-+.0-9\]+ ?\[\}\] (def|bd|bind def)" {
			append prolog [I get output]
			lappend centers [I get match 1]
			I continue
		} "/(\[^ \]+) \[\{\] ?\[0-9\]+ (\[^\}\n\]+)\[\}\] (def|bd|bind def)" {
			append prolog [I get output]
			if {[string equal set_country [lindex [I get match 2] end]]} {
				lappend powers [I get match 1]
			}
			I continue
		} "/(\[^ \]+) \\(\\w\\) /(\[^ \]+) /(\[^ \]+) AddCountry" {
			append prolog [I get output]
			lappend powers [I get match 1]
			I continue
		} "/(\[^ \]+) <<" {
			set center [I get match 1]
			append prolog [I get output]
			I expect fill "/SupplyPlace" {
				append prolog [I get output]
				lappend centers $center
				I continue
			} ">>" {
				append prolog [I get output]
			}
			I continue
		} "/(Visit)?Territories \[\{\]" {
			append prolog [I get output]
			I expect fill "(\[-+.0-9\]+ \[-+.0-9\]+) \[(\](\[^)\]*)\[)\] .\n" {
				append prolog [I get output]
				set territories([I get match 2]) [I get match 1]
				I continue
			} "\[\}\] (bd|def)\n" {
		 		append prolog [I get output]
			}
			I continue
		} "/(Visit)?(Centers|Supplies) \[\{\]" {
			append prolog [I get output]
			I expect fill "\[-+.0-9\]+ \[-+.0-9\]+ /(\[^ \]*) .\n" {
				append prolog [I get output]
				lappend centers [I get match 1]
				I continue
			} "/(\[^ \]*) \[-+.0-9\]+ \[-+.0-9\]+ .\n" {
				append prolog [I get output]
				lappend centers [I get match 1]
				I continue
			} "\[\}\] (bd|def)\n"  {
		 		append prolog [I get output]
			}
			I continue
		} "/(Visit)?Powers \[\{\]" {
			append prolog [I get output]
			I expect fill "/(\[^ \]+) \[(\].\[)\] \[^ \]+ \[^ \]+ (\\(\[^)\]*\\) )*\[^ \]\n" {
				append prolog [I get output]
				lappend powers [I get match 1]
				I continue
			} "\[\}\] (bd|def)\n" {
		 		append prolog [I get output]
			}
			I continue
		} "/DrawNames \[\{\]" {
			append prolog [I get output]
			set drawNames 0
			I continue
		} "/([regsub -all "\\s+" [array names commands] "|"]) \[\{\]" {
			append prolog [I get output]
			set commands([I get match 1]) 1
			I continue
		} eof {
			append prolog [string trimright [I get output]]
		}
		
		itcl::delete object I
		
		informIf $unsupportedInfo {Unsupported: [regsub -all "\\w+\\s+1\\s*|\\s+0" [array get commands] ""]}
		
		set g [open $gameFile w]
		if {[string length $encoding]} {
			fconfigure $g -encoding $encoding
		}
		puts $g $prolog
		
		if {$drawNames} {
			puts $g "/DrawNames {"
			foreach province [lsort [array names territories]] {
				if {[string last "/" $province] < 1} {
					puts $g "$territories($province) ($province) DrawName"
				}
			}
			puts $g "} def"
		}
		
		iexpect::Expect I -file $gameRecordFile -white -nocase -encoding $encoding
		
		array set draws "A DrawArmy F DrawFleet W DrawWing B DrawBoat"
		array set actions "0 REMOVES 1 BUILDS"
		array set holdResults "! {} ~ DISLODGED * DESTROYED"
		array set moveResults "! {} ? {NO CONVOY} | BOUNCE ~ DISLODGED * DESTROYED"
		array set supportResults "! {} ? VOID | CUT ~ DISLODGED * DESTROYED"
		array set convoyResults "! {} ? VOID ~ DISLODGED * DESTROYED"
		
		set pages 0
		set gameTitle [file tail [file rootname $gameRecordFile]]
		set title ""
		set neutrals $centers
		set season ""
		set ownerStatus 0
		
		set reProvince "\[^ \]+"
		set reUnit "\[[string tolower [join [array names draws]]]\]"
		set reRetreat "r (.*)(\[!?*\])"
		
		I expect line "^$" {
			I continue
		} -case "^%# Title: " {
			set gameTitle [string trim [I get unchecked]]
			I continue
		} -case "^%# OwnerStatus: (Before|After)$" {
			set ownerStatus [string equal [I get match 1] After]
			I continue
		} "^%" {
			I continue
		} "^($reProvince) (\[$^`\])$" {
			set idx 0
			set supply [string toupper [I get match [incr idx]]]
			set operator [I get match [incr idx]]
			if {![info exists country]} {
				mention "Country not set for center $supply"
			} elseif {[lsearch $centers $supply] == -1} {
				mention "Unknown SC $supply"
			} elseif {![string equal $operator "`"]} {
				if {[set supplied [lsearch $neutrals $supply]] == -1} {
					mention "SC $supply already assigned"
				} else {
					set neutrals [lreplace $neutrals $supplied $supplied]
					lappend supplies($country) $supply
				}
			}
			I continue
		} "^($reUnit) ($reProvince) (\[!?+*\]|\\\?\\\?|!!)$" {
			set idx 0
			set unit [string toupper [I get match [incr idx]]]
			set province [string toupper [I get match [incr idx]]]
			set operator [I get match [incr idx]]
			adjust $unit $province $operator
			I continue
		} "^($reUnit) ($reProvince) h (\[!~*\]|($reRetreat))$" {
			set idx 0
			set unit [string toupper [I get match [incr idx]]]
			set province [string toupper [I get match [incr idx]]]
			set operator [I get match [incr idx]]
			if {[string length [I get match [incr idx]]]} {
				hold $unit $province "~"
				retreat $unit $province [string toupper [I get match [incr idx]]] [I get match [incr idx]]
			} else {
				hold $unit $province $operator
			}
			I continue
		} "^($reUnit) ($reProvince) s ($reProvince) (\[!?|~*\]|($reRetreat))$" {
			set idx 0
			set unit [string toupper [I get match [incr idx]]]
			set province [string toupper [I get match [incr idx]]]
			set origin [string toupper [I get match [incr idx]]]
			set operator [I get match [incr idx]]
			if {[string length [I get match [incr idx]]]} {
				supportHold $unit $province $origin "~"
				retreat $unit $province [string toupper [I get match [incr idx]]] [I get match [incr idx]]
			} else {
				supportHold $unit $province $origin $operator
			}
			I continue
		} "^($reUnit) ($reProvince) s ($reProvince) ($reProvince) (\[!?|~*\]|($reRetreat))$" {
			set idx 0
			set unit [string toupper [I get match [incr idx]]]
			set province [string toupper [I get match [incr idx]]]
			set origin [string toupper [I get match [incr idx]]]
			set destination [string toupper [I get match [incr idx]]]
			set operator [I get match [incr idx]]
			if {[string length [I get match [incr idx]]]} {
				supportMove $unit $province $origin $destination "~"
				retreat $unit $province [string toupper [I get match [incr idx]]] [I get match [incr idx]]
			} else {
				supportMove $unit $province $origin $destination $operator
			}
			I continue
		} "^($reUnit) ($reProvince) c ($reProvince) ($reProvince) (\[!?~*\]|($reRetreat))$" {
			set idx 0
			set unit [string toupper [I get match [incr idx]]]
			set province [string toupper [I get match [incr idx]]]
			set origin [string toupper [I get match [incr idx]]]
			set destination [string toupper [I get match [incr idx]]]
			set operator [I get match [incr idx]]
			if {[string length [I get match [incr idx]]]} {
				convoy $unit $province $origin $destination "~"
				retreat $unit $province [string toupper [I get match [incr idx]]] [I get match [incr idx]]
			} else {
				convoy $unit $province $origin $destination $operator
			}
			I continue
		} "^($reUnit) ($reProvince) $reRetreat$" {
			set idx 0
			set unit [string toupper [I get match [incr idx]]]
			set province [string toupper [I get match [incr idx]]]
			set destinations [string toupper [I get match [incr idx]]]
			set operator [I get match [incr idx]]
			retreat $unit $province $destinations $operator
			I continue
		} "^($reUnit) ($reProvince) ($reProvince) (\[!?|~*\]|($reRetreat))$" {
			set idx 0
			set unit [string toupper [I get match [incr idx]]]
			set province [string toupper [I get match [incr idx]]]
			set destination [string toupper [I get match [incr idx]]]
			set operator [I get match [incr idx]]
			if {[string length [I get match [incr idx]]]} {
				move $unit $province $destination "~"
				retreat $unit $province [string toupper [I get match [incr idx]]] [I get match [incr idx]]
			} else {
				move $unit $province $destination $operator
			}
			I continue
		} -case "^ShowPage$" {
			showPage
			I continue
		} -case "^End$" {
			showPage
			I continue
		} -case "^(\[(\](.*)\[)\] )?(-?\\d+ )?((\[A-Z\]\[a-z\]+)+)$" {
			if {[string length $title]} {showPage}
			set season [I get match 4]
			if {[string length [I get match 1]]} {
				set title [I get match 2]
			} else {
				set title $gameTitle
			}
			set title "$title,[regsub -all "\[A-Z\]" $season " \\0"] [I get match 3]"
			I continue
		} "^(_?($reProvince)) v (_?($reProvince))$" {
			if {[lsearch $powers [string toupper [I get match 2]]] == -1} {
				mention "Controlling power \"[string toupper [I get match 2]]\" not defined"
				if {[lsearch $powers [string toupper [I get match 4]]] == -1} {
					mention "Controlled power \"[string toupper [I get match 4]]\" not defined"
				} else {
					set country [string toupper [I get match 3]]
				} 
			} elseif {[lsearch $powers [string toupper [I get match 4]]] == -1} {
				mention "Controlled power \"[string toupper [I get match 4]]\" not defined"
			} else {
				set country [list [string toupper [I get match 3]] [string toupper [I get match 1]]]
			} 
			if {![info exists survivors($country)]} {
				set survivors($country) 0
			}
			I continue
		} "^_?($reProvince)$" {
			if {[lsearch $powers [string toupper [I get match 1]]] == -1} {
				mention "Can't interpret \"[string toupper [I get match 1]]\""
			} else {
				set country [string toupper [I get match]]
			}
			if {![info exists survivors($country)]} {
				set survivors($country) 0
			}
			I continue
		} default {
			mention "Can't interpret \"[I get output]\""
			I continue
		}
		
		itcl::delete object I
		
		puts $g "%%PageTrailer"
		puts $g "%%Trailer"
		puts $g "%%Pages: $pages 1"
		puts $g "%%BoundingBox: $bbox"
		close $g
	}
	
}

# Check arguments.

if {[llength $argv]} {
	set helpInfo {
	 	"Usage: process.tcl [-?] [-d] [-u] [--] variant.ps game.txt [game.ps]"
		"\t-?: Display this help message"
		"\t-d: Print out every action"
		"\t-u: Print out all unsupported procedures"
		"\t--: No more switches to follow"	
	}

	set displayInfo 0
	set unsupportedInfo 0
	while {[llength $argv] && [regexp "^\[-/\](.)$" [lindex $argv 0] switching swi]} {
		set argv [lrange $argv 1 end]
		switch -- $swi d {
			set displayInfo 1
		} u {
			set unsupportedInfo 1
		} ? {
			puts [join $helpInfo "\n"]
			exit
		} - {
			break
		} default {
			Process::mention "Switch <$swi> not supported." $helpInfo
			exit
		}
	}
	
	set arguments $argv
	set argv {}
	switch [llength $arguments] 2 {
		lappend arguments {}
	} 3 {
	} default {
		Process::mention "Either 2 or 3 arguments needed, the name of a variant .ps file, the name of a DPeye style game record .txt file and optionally the name of the resulting .ps file." $helpInfo
		exit
	}

	eval Process::process $arguments $displayInfo $unsupportedInfo
}